package main

import (
	"context"
	"fmt"
	"golang.org/x/sync/errgroup"
	"io"
	"log"
	"math"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"sync/atomic"
	"syscall"
	"time"
	"week3/server"
)

// RootCtx 根context
var RootCtx context.Context
var RootCancel func()

func init() {
	// 根上下文和取消 主要用于关闭所有请求里面grountine
	RootCtx, RootCancel = context.WithCancel(context.Background())
}

func main() {

	// 监听系统关闭信号
	var waiter = make(chan os.Signal, 1) // 按文档指示，至少设置1的缓冲
	signal.Notify(waiter, syscall.SIGTERM, syscall.SIGINT)

	// 创建一个errgroup
	eg, egCtx := errgroup.WithContext(RootCtx)

	// 创建2个服务器
	srv := server.CreatHttpService(18078)
	srv1 := server.CreatHttpService(18077)
	// 关闭总服务，只需要执行一次
	var once sync.Once
	// 杀掉所有资源
	onceBody := func() {

		// 杀掉所有依赖上下文的请求
		RootCancel()

		// 设备监听关闭超时，http标准库Shutdown里面是需要传入一个context的。以防止可能的意外发生
		// context.WithDeadline(ctx, time.Now().Add(5*time.Second))
		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer cancel()
		// 关闭服务
		if err := srv.Close(ctx); err != nil {
			fmt.Println("1" + err.Error())
		}
		if err := srv1.Close(ctx); err != nil {
			fmt.Println("2" + err.Error())
		}

		fmt.Println("关闭所有服务监听")
	}

	// 赋值路由
	err := srv.RegisterHandle("/test", HelloWorld)
	if err != nil {
		log.Fatalln(err)
	}
	err = srv1.RegisterHandle("/test", HelloWorld1)
	if err != nil {
		log.Fatalln(err)
	}

	// 启动2个http监听
	eg.Go(func() error {
		return srv.Open()
	})
	eg.Go(func() error {
		return srv1.Open()
	})

	// 等待系统关闭信号
	eg.Go(func() error {
		select {
		case <-waiter:
			once.Do(onceBody)
		}
		return nil
	})

	// 一个监听服务退出，所有的服务都注销退出
	eg.Go(func() error {
		select {
		case <-egCtx.Done(): // 2个监听服务出错会进入到这里保证推出。
			fmt.Printf("3 %v \n", egCtx.Err().Error())
			once.Do(onceBody)
		}
		return nil
	})

	// 等待所有Grountine退出
	if erreg := eg.Wait(); erreg != nil {
		fmt.Println("退出原因" + erreg.Error())
	}
	fmt.Println("程序被关闭")
}

func HelloWorld(w http.ResponseWriter, r *http.Request) {
	eg, ctx := errgroup.WithContext(RootCtx)

	// 模拟数据操作
	var sum int64 = 0

	eg.Go(func() error {
		io.WriteString(w, "hello world1\n")
		return nil
	})

	// 模拟可能出现的多个耗时的Grountine
	eg.Go(func() error {
		for {
			atomic.AddInt64(&sum, 2)
			select {
			case <-ctx.Done():
				fmt.Println("HelloWorld方法1" + ctx.Err().Error())
				return nil
			default:
				time.Sleep(10 * time.Millisecond)
				atomic.CompareAndSwapInt64(&sum, math.MaxInt64, 0)
			}
		}
	})
	eg.Go(func() error {
		for {
			// TODO
			atomic.AddInt64(&sum, -1)
			select {
			case <-ctx.Done():
				fmt.Println("HelloWorld方法2" + ctx.Err().Error())
				return nil
			default:
				time.Sleep(10 * time.Millisecond)
			}
		}
	})

	eg.Wait()
	fmt.Println(atomic.LoadInt64(&sum))
	fmt.Println("HelloWorld方法退出")
}

// 模拟sync.pool的使用
var createNum int32

func createBuffer() interface{} {
	atomic.AddInt32(&createNum, 1)
	buffer := make([]byte, 1024)
	return buffer
}
func HelloWorld1(w http.ResponseWriter, r *http.Request) {
	eg := errgroup.Group{}

	// 模拟context的WithValue 存map的COW
	value := make(map[string]string)
	value["test"] = "tinghao"
	ctx := context.WithValue(context.Background(), "keymap", value)

	eg.Go(func() error {
		ma := ctx.Value("keymap").(map[string]string)
		io.WriteString(w, "hello world1"+ma["test"]+"\n")

		maCow := mapCOW(ma)
		maCow["cow"] = "cowtest"
		ctx := context.WithValue(context.Background(), "keymap", maCow)
		eg.Go(func() error {
			ma := ctx.Value("keymap").(map[string]string)
			io.WriteString(w, "test"+ma["test"]+"\n")
			io.WriteString(w, "cow"+ma["cow"]+"\n")
			return nil
		})
		return nil
	})

	// 模拟使用sync.pool
	bufferPool := &sync.Pool{New: createBuffer}
	workerPool := 1024 * 1024
	for i := 0; i < workerPool; i++ {
		eg.Go(func() error {
			buffer := bufferPool.Get()
			defer bufferPool.Put(buffer)
			_ = buffer.([]byte)
			// TODO buffer
			return nil
		})
	}
	eg.Wait()
	// 看看最终能创建几次。是不是做到复用了
	io.WriteString(w, fmt.Sprintf("%d buffer objects were create.\n", atomic.LoadInt32(&createNum)))
	fmt.Println("HelloWorld1方法退出")

	//返回结果012
	//hello world1tinghao
	//testtinghao
	//cowcowtest
	//7 buffer objects were create.      因机器不一样，结果也会不一样

}

func mapCOW(ma map[string]string) map[string]string {
	targetMap := make(map[string]string)
	// Copy from the original map to the target map
	for key, value := range ma {
		targetMap[key] = value
	}
	return targetMap
}
